#ifndef _CSLIDERWIDGET_H_
#define _CSLIDERWIDGET_H_

#include <buola/widgets/cwidget.h>

#include <buola/utility/uevent.h>

namespace buola { namespace gui {

///\addtogroup widgetclass
///@{

////////////////////////////////////////////////////////////////////////////

/// slider widget class

/// this is a base class for widgets that display a numeric value which
/// can be modified by dragging a bar
///
/// it is templatised on the type of the numeric value
///
////////////////////////////////////////////////////////////////////////////

template <typename tValue>
class CSliderWidget : public CWidget
{
public:
    //! style flags
    enum
    {
        STYLE_HORIZONTAL=(1<<8),    //! display horizontally
        STYLE_REVERSE=(1<<9)        //! reverse where max and min values appear
    };

protected:
//! \name constructor and destructor
//@{
    CSliderWidget()
        :   mSWMin(0),
            mSWMax(100),
            mSWValue(0),
            mSWSmallStep(1),
            mSWBigStep(5)
    {}

    virtual ~CSliderWidget()
    {}
//@}

public:
//! \name properties
//@{
    void GetPropertyNames(std::vector<std::string> &pNames)
    {
        pNames.push_back("horizontal");

        CWidget::GetPropertyNames(pNames);
    }

    void SetProperty(const std::string &p,const UAny &v)
    {
        if(ProcessStyleProperty(p,v,"horizontal",STYLE_HORIZONTAL)) return;

        CWidget::SetProperty(p,v);
    }

//! \name public interface
//@{
    //! set the minimum value for the slider
    void SetMin(tValue pMin)
    {
        mSWMin=pMin;

        if(mSWValue<pMin)
            mSWValue=pMin;

        OnSWChange();
    }
    //! set the maximum value for the slider
    void SetMax(tValue pMax)
    {
        mSWMax=pMax;

        if(mSWValue>pMax)
            mSWValue=pMax;

        OnSWChange();
    }
    //! set the maximum and minimum values for the slider
    void SetRange(tValue pMin,tValue pMax)
    {
        mSWMin=pMin;
        mSWMax=pMax;

        if(mSWValue<pMin)
            mSWValue=pMin;
        else if(mSWValue>pMax)
            mSWValue=pMax;

        OnSWChange();
    }

    //! set the step for the slider
    void SetStep(tValue pSmall,tValue pBig=0)
    {
        mSWSmallStep=pSmall;
        mSWBigStep=pBig?pBig:pSmall;

        OnSWChange();
    }

    //! set the current value of the slider
    void SetValue(tValue pValue,bool pEvent=true)
    {
        if(pValue<mSWMin)
            pValue=mSWMin;
        else if(pValue>mSWMax)
            pValue=mSWMax;

        if(pValue==mSWValue) return;

        mSWValue=pValue;

        OnSWChange();

        if(pEvent)
            eChanged(mSWValue);
    }

    //! increments the value by the given amount
    void IncValue(tValue pInc)
    {
        if(pInc==0) return;

        SetValue(mSWValue+pInc);
    }

    //! get the minimum value
    tValue GetMin()         {   return mSWMin;                          }
    //! get the maximum value
    tValue GetMax()         {   return mSWMax;                          }
    //! get the current value
    tValue GetValue()       {   return mSWValue;                        }
    //! get the small step
    tValue GetSmallStep()   {   return mSWSmallStep;                    }
    //! get the big step
    tValue GetBigStep()     {   return mSWBigStep;                      }

    //! true if min==max
    bool EmptyRange()       {   return mSWMin==mSWMax;                  }
//@}

//! \name interface for reimplementors
//@{
private:
    //! reimplement to keep track of changes
    virtual void OnSWChange()=0;

protected:
    //! set the value without triggering an update
    void SWSetValue(tValue pValue)
    {
        mSWValue=pValue;
    }

    //! set the range without triggering an update
    void SWSetRange(tValue pMin,tValue pMax)
    {
        mSWMin=pMin;
        mSWMax=pMax;
    }

    //! maps from position to value
    tValue SWMapToValue(tValue pMinPos,tValue pMaxPos,tValue pPos)
    {
        int lPosRange=pMaxPos-pMinPos;
        int lPos=pPos-pMinPos;

        if(GetStyle(STYLE_REVERSE))
            lPos=lPosRange-lPos;

        return mSWMin+(mSWMax-mSWMin)*lPos/lPosRange;
    }

    //! maps from value to position
    tValue SWMapToPos(tValue pMinPos,tValue pMaxPos)
    {
        int lPosRange=pMaxPos-pMinPos;

        int lPos=(mSWMax>mSWMin)?(lPosRange*(mSWValue-mSWMin)/(mSWMax-mSWMin)):0;

        if(GetStyle(STYLE_REVERSE))
            lPos=lPosRange-lPos;

        return lPos+pMinPos;
    }
//@}

public:
//! \name events and signals
//@{
    //! triggered when the value of the slider changes
    UEvent<void(tValue)> eChanged;
//@}

private:
    tValue mSWMin;
    tValue mSWMax;
    tValue mSWValue;
    tValue mSWSmallStep;
    tValue mSWBigStep;
};

///@}

/*namespace gui*/ } /*namespace buola*/ }

#endif
